<template>
  <div
    ref="dropZone"
    class="overflow-hidden"
    :class="{
      'border-dashed border-success': isOverDropZone,
    }"
    :style="style"
    @click="($refs.file as HTMLElement).click()"
  >
    <input
      ref="file"
      type="file"
      :accept="accept"
      class="hidden"
      @change="onFileChange"
    />
    <img
      v-if="src"
      class="h-full w-full object-cover"
      :src="src"
      alt="preview"
    />
    <slot v-else>
      <svg
        xmlns="http://www.w3.org/2000/svg"
        class="h-full w-full text-gray-400"
        viewBox="0 0 24 24"
      >
        <path
          fill="currentColor"
          d="M15.71 12.71a6 6 0 1 0-7.42 0a10 10 0 0 0-6.22 8.18a1 1 0 0 0 2 .22a8 8 0 0 1 15.9 0a1 1 0 0 0 1 .89h.11a1 1 0 0 0 .88-1.1a10 10 0 0 0-6.25-8.19ZM12 12a4 4 0 1 1 4-4a4 4 0 0 1-4 4Z"
        />
      </svg>
    </slot>
  </div>
</template>
<script setup lang="ts">
import { ref, watch } from "vue";
import { useDropZone } from "@vueuse/core";
import { isString } from "../utils";

type Props = Readonly<{
  width?: string;
  height?: string;
  accept?: string;
  modelValue?: string | File;
  maxSize?: number;
  name?: string;
}>;

const props = withDefaults(defineProps<Props>(), {
  accept: "image/png, image/jpeg, image/gif, image/jpg, image/avif, image/webp",
});

const emit = defineEmits<{
  "update:modelValue": [File];
  error: [{ [key: string]: string[] }];
}>();

const style = computed(() => {
  const style = {} as Record<string, string>;
  if (props.width) style.width = props.width;
  const height = props.height ?? props.width;
  if (height) style.height = height;
  return style;
});

const src = ref(""),
  dropZone = ref(null);

const isAccept = (file: File) =>
  props.accept
    ? props.accept
        .split(",")
        .map((i) => i.trim())
        .includes(file.type)
    : true;

const onSelectedFile = (files: File[] | null) => {
  if (!files?.length) return;
  const file = files[0];
  // 判断文件类型
  if (!isAccept(file)) {
    if (props.name) emit("error", { [props.name]: ["不支持的文件类型"] });
    return;
  }
  // 判断文件大小是否超出限制 KB
  if (props.maxSize && file.size / 1024 > props.maxSize) {
    if (props.name)
      emit("error", {
        [props.name]: [`文件大小超出${props.maxSize}KB限制`],
      });
    return;
  }
  // 读取文件
  src.value = URL.createObjectURL(file);
  emit("update:modelValue", file);
};
// 拖拽
const { isOverDropZone } = useDropZone(dropZone, onSelectedFile);

watch(
  () => props.modelValue,
  (value) => {
    if (isString(value)) src.value = value ?? "";
  },
);
const onFileChange = (e: Event) => {
  const target = e.target as HTMLInputElement;
  if (target.files) onSelectedFile([...target.files]);
};
</script>
